package com.google.code.pathlet.eshop.config.web;

import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.pathlet.config.anno.ContainerIn;
import com.google.code.pathlet.config.anno.InstanceIn;
import com.google.code.pathlet.config.anno.SpaceIn;
import com.google.code.pathlet.core.InstanceSpace;
import com.google.code.pathlet.core.PathletContainer;
import com.google.code.pathlet.eshop.config.vo.DataSourceConfig;
import com.google.code.pathlet.jdbc.ExtJdbcTemplate;
import com.google.code.pathlet.util.ClassPathResource;
import com.google.code.pathlet.util.ClassUtils;
import com.google.code.pathlet.util.ScriptRunner;
import com.google.code.pathlet.util.ValueUtils;
import com.google.code.pathlet.web.PathletAjaxProcessor;
import com.google.code.pathlet.web.WebConfigManagerAccessor;
import com.google.code.pathlet.web.WebPathletConstants;
import com.google.code.pathlet.web.widget.ResponseLevel;
import com.google.code.pathlet.web.widget.ResponseMessage;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DataSourceAction {
	
	public final static String DATABASE_TYPE_MYSQL = "MySQL";
	
	public final static String DATABASE_TYPE_H2 = "H2";
	
	private static Log log = LogFactory.getLog(DataSourceAction.class);
	
	@ContainerIn
	private PathletContainer container;
	
	@SpaceIn(scope="request")
	private InstanceSpace requestSpace;
 
	@InstanceIn(path="/dataSource.pb")
	private ComboPooledDataSource dataSource;
	
	private ExtJdbcTemplate jdbcTemplate = null;
	
	public ExtJdbcTemplate getJdbcTemplate()  {
		if(this.jdbcTemplate == null) {
			this.jdbcTemplate = new ExtJdbcTemplate(dataSource);
		}
		return this.jdbcTemplate;
	}
	
	private DataSourceConfig config;
	
	public DataSourceConfig getConfig() {
		return config;
	}

	public void setConfig(DataSourceConfig config) {
		this.config = config;
	}

	public DataSourceConfig getCurrentConfig() {
		DataSourceConfig config = new DataSourceConfig();
		
		config.setJdbcUrl(dataSource.getJdbcUrl());
		config.setDriverClass(dataSource.getDriverClass());
		config.setUsername(dataSource.getUser());
		config.setPassword(dataSource.getPassword());
		
		if(dataSource.getDriverClass().startsWith("com.mysql")) {
			config.setDatabaseType(DATABASE_TYPE_MYSQL);
		}
		else if(dataSource.getDriverClass().startsWith("org.h2")) {
			config.setDatabaseType(DATABASE_TYPE_H2);
		}
		
		if(ValueUtils.notEmpty(dataSource.getJdbcUrl()) 
				&& ValueUtils.notEmpty(dataSource.getDriverClass())
				&& ValueUtils.notEmpty(dataSource.getUser())) {
			//Check wether has initialized data.
			try {
				int userRowCount = getJdbcTemplate().queryForInt("select count(USERNAME) from T_USER");
				if(userRowCount > 0) {
					config.setDataInitialized(true);
				}
			}
			catch(Throwable t) {
				/* ignore any exception for this testing operation */
			}
		}
		
		return config;
	}
	
	public DataSourceConfig getH2DefaultConfig() {
		DataSourceConfig config = new DataSourceConfig();
		config.setDatabaseType(DATABASE_TYPE_H2);
		config.setDriverClass("org.h2.Driver");
		config.setJdbcUrl("jdbc:h2:file:~/h2pathlet");
		config.setUsername("sa");
		config.setPassword("sa");
		return config;
	}
	
	public DataSourceConfig getMySQLDefaultConfig() {
		DataSourceConfig config = new DataSourceConfig();
		config.setDatabaseType(DATABASE_TYPE_MYSQL);
		config.setDriverClass("com.mysql.jdbc.Driver");
		config.setJdbcUrl("jdbc:mysql://localhost/eshop?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8");
		config.setUsername("root");
		config.setPassword("");
		return config;
	}
	
	public ResponseMessage testConnection() throws SQLException {
		ComboPooledDataSource ds = new ComboPooledDataSource();
		Connection conn = null;
		try {
			Class driverClass = ClassUtils.getDefaultClassLoader().loadClass(config.getDriverClass());
			if(java.sql.Driver.class.isAssignableFrom(driverClass) == false) {
				throw new ClassNotFoundException("Driver Class is not illegal JDBC Driver class!");
			}
			
			ds.setDriverClass(config.getDriverClass());
			ds.setJdbcUrl(config.getJdbcUrl());
			ds.setUser(config.getUsername());
			ds.setPassword(config.getPassword());
			ds.setInitialPoolSize(1);
			ds.setMinPoolSize(1);
			ds.setMaxPoolSize(2);
			ds.setAcquireRetryAttempts(1);
			ds.setIdleConnectionTestPeriod(10);
			ds.setAcquireIncrement(1);
			ds.setMaxStatements(0);
			
			conn = ds.getConnection();
			
			if(conn == null) {
				throw new SQLException(""); 
			}
		} 
		catch (SQLException e) {
			return new ResponseMessage(ResponseLevel.ERROR, "Failed to access connection! <br/>" + e.getMessage());
		} catch (PropertyVetoException e) {
			return new ResponseMessage(ResponseLevel.ERROR, "Error configuration! Please check input fields! <br/>" + e.getMessage());
		} catch (ClassNotFoundException e) {
			return new ResponseMessage(ResponseLevel.ERROR, "Error configuration! Please check input fields! <br/>" + e.getMessage());
		}
		finally {
			if(conn != null) {
				try { conn.close(); } catch (SQLException e) { throw e;  }
			}
		}
		
		return new ResponseMessage(ResponseLevel.INFO, "Test succeeded!");
	}
	
	
	public ResponseMessage applyConfig() throws IOException  {
		
		log.info("Reloading the PathletContainer ...");
		HttpServletRequest request = (HttpServletRequest)requestSpace.getInstance(WebPathletConstants.REQUEST_PATH);
		
		container.destroy();
		
		//Reload the configuration and reset the ConfigManager of the PathletContainer
		WebConfigManagerAccessor configAccessor = WebConfigManagerAccessor.getInstance(request.getSession().getServletContext());
		
		Properties prop = new Properties();		
		prop.setProperty("dataSource.driverClass", config.getDriverClass());
		prop.setProperty("dataSource.jdbcUrl", config.getJdbcUrl());
		prop.setProperty("dataSource.username", config.getUsername());
		prop.setProperty("dataSource.password", config.getPassword());
		
		savePlaceholder(prop, configAccessor.getPropertyFiles()[0]);
		
		container.setConfigManager(configAccessor.loadConfigManager());
		
		container.init();
		
		//After container.destroy() be invoked, the all InstanceSpace would be removed from container, includes InstanceSpace of this request. 
		//To make available InstanceSpace for this request, the new InstanceSpace should be created.
		container.getSpace(WebPathletConstants.REQUEST_SCOPE, request, true);
		
		log.info("The PathetContainer successfully reloaded!");
		
		return new ResponseMessage(ResponseLevel.INFO, "DataSource configuration completely applied!");
	}
	
	/**
	 * Import the database definitions and data from the SQL script.
	 * 
	 * @return
	 * @throws SQLException
	 * @throws IOException
	 */
	public ResponseMessage initialzeDatabase() throws SQLException, IOException {
		
		ScriptRunner scriptRunner = new ScriptRunner(dataSource.getConnection(), true, true);

		InputStream is = new ClassPathResource("/init.sql", this.getClass().getClassLoader()).getInputStream();
		InputStreamReader reader = new InputStreamReader(is, "UTF-8");
		if(scriptRunner.runScript(reader)) {
			return new ResponseMessage(ResponseLevel.INFO, "Data initialization complete!");
		}
		else {
			return new ResponseMessage(ResponseLevel.ERROR, "Found error in data initialization process. For more information please see the console log!");
		}
			
	}
	
	public void savePlaceholder(Properties prop, File propFile) throws IOException {
		Writer propertiesWriter = null;
		try {
			prop.store(new FileOutputStream(propFile), "Pathlet DataSourceAction automatically saved.");
		}
		finally {
			if(propertiesWriter != null) propertiesWriter.close();
		}
	}
	

	
}